#include	"a_global_vars.h"
#include	"config_parser.h"
//------------------------------------------------------------------------------------
struct timeval startTime, endTime, performance_start_time, performance_end_time, tv1, tv2;
int PROCESS_ID, self_sid, successor_sid, SUBMIT_PER_WAKE;
int lowestDelivered = -1, gapEnd = -1;
bool COORDINATOR = false, rtag = false;

inst *data;
paxos_data* paxos_instances;

connection acceptor_connections[1];
int acceptor_connection_cnt = 0;
inst *new_data;
const char *config_file_path;
//------------------------------------------------------------------------------------
void main_initialize();
void common_event_init();
void initialize();
void validate_input_parameters(int argc, const char * argv[]);
void clean_exit(int sig);
void enable_ctrl_c_handler();
//------------------------------------------------------------------------------------
int main(int argc, const char *argv[]) {
	int status;

	main_initialize();
	validate_input_parameters(argc, argv);
	parse_configuration(config_file_path, PROCESS_ID, -1, -1);
	enable_ctrl_c_handler();

	successor_sid = GET_ACCEPTOR_SUCCESSOR_SID_RING(PROCESS_ID);
	self_sid = GET_ACCEPTOR_SELF_SID_RING(PROCESS_ID);

	event_init();

	printf("-----------------------------------------------------------\n");
	if (PROCESS_ID == 0) {

		COORDINATOR = true;
		initialize();
		common_event_init();
		//--------------------Events------------------------------------------
		evtimer_set(inst_propose_timeout_eve, prepare_fresh_instances_timer_event, inst_propose_timeout_eve);
		status = evtimer_add(inst_propose_timeout_eve, &tv1);
		assert(status >= 0);

		event_set(acceptor_0_recv_msg_eve, self_sid, EV_READ | EV_PERSIST, acceptor_0_recv_msg, NULL);
		status = event_add(acceptor_0_recv_msg_eve, NULL);
		assert(status >= 0);

	} else {

		initialize();
		common_event_init();

		event_set(recv_ring_sock_eve, GET_ACCEPTOR_SELF_SID_RING(PROCESS_ID), EV_READ | EV_PERSIST, acceptor_msg_type_recognizer, NULL);
		status = event_add(recv_ring_sock_eve, NULL);
		assert(status >= 0);
	}
	event_dispatch();
	return 0;
}
void main_initialize() {
	tv2.tv_sec = 1;
	tv2.tv_usec = 0;
}
void common_event_init() {

	recv_ring_sock_eve = (struct event*) malloc(sizeof(struct event));
	inst_propose_timeout_eve = (struct event*) malloc(sizeof(struct event));
	print_stat_timeout_eve = (struct event*) malloc(sizeof(struct event));
	acceptor_0_recv_msg_eve = (struct event*) malloc(sizeof(struct event));

	int status;

	gettimeofday(&startTime, NULL);
	gettimeofday(&performance_start_time, NULL);

	evtimer_set(print_stat_timeout_eve, compute_performance, print_stat_timeout_eve);
	status = evtimer_add(print_stat_timeout_eve, &tv2);
	assert(status >= 0);

}
void initialize() {

	N = get_config_info()->total_num_acceptors;

	a_interval_performance = malloc(sizeof(a_performance));
	a_next_performance_index = 0;
	a_nulify_performance(a_interval_performance);
	a_interval_performance->highest_deliverable_inst_id = -1;

	data = (inst*) malloc(sizeof(inst) * INTERNAL_BUFFER_SIZE);
	new_data = (inst*) malloc(sizeof(inst));
	paxos_instances = (paxos_data*) malloc(sizeof(paxos_data) * INTERNAL_BUFFER_SIZE);
	p_msg = (proposer_msg*) malloc(sizeof(proposer_msg));

	int i;
	for (i = 0; i < INTERNAL_BUFFER_SIZE; i++) {
		data[(i % INTERNAL_BUFFER_SIZE)].deliver = false;
		data[(i % INTERNAL_BUFFER_SIZE)].received = false;
		data[(i % INTERNAL_BUFFER_SIZE)].highestDeliverable = -1;
		data[(i % INTERNAL_BUFFER_SIZE)].packet.p_msgs[0].msg_id = 0;
	}

	if (!COORDINATOR) {
		for (i = 0; i < INTERNAL_BUFFER_SIZE; i++) {
			paxos_instances[(i % INTERNAL_BUFFER_SIZE)].rnd = 0;
			paxos_instances[(i % INTERNAL_BUFFER_SIZE)].vrnd = 0;
			paxos_instances[(i % INTERNAL_BUFFER_SIZE)].vval = 0;
			paxos_instances[(i % INTERNAL_BUFFER_SIZE)].crnd = -1;
			paxos_instances[(i % INTERNAL_BUFFER_SIZE)].cval = -1;
		}
	} else {
		for (i = 0; i < INTERNAL_BUFFER_SIZE; i++) {
			paxos_instances[(i % INTERNAL_BUFFER_SIZE)].rnd = 0;
			paxos_instances[(i % INTERNAL_BUFFER_SIZE)].vrnd = 0;
			paxos_instances[(i % INTERNAL_BUFFER_SIZE)].vval = 0;
			paxos_instances[(i % INTERNAL_BUFFER_SIZE)].crnd = 1;// before =0
			paxos_instances[(i % INTERNAL_BUFFER_SIZE)].cval = 0;
		}
	}
}
void validate_input_parameters(int argc, const char * argv[]) {

	if (argc != 6) {

		printf("Incompatible call to this function. Try Again.!\n<1. PROCESS_ID>\n<2. Config file >\n<3. Sending Interval(ms)>\n<4. Num of Packets/Interval>\n<5. Performance File>\n");
		exit(-1);

	} else {

		PROCESS_ID = atoi(argv[1]);
		config_file_path = argv[2];
		tv1.tv_usec = atoi(argv[3]);
		SUBMIT_PER_WAKE = atoi(argv[4]);
		a_performance_file = argv[5];
		tv1.tv_sec = 0;
	}
}

void clean_exit(int sig) {
	if (sig == SIGINT)
		signal(SIGINT, clean_exit);
	if (sig == SIGABRT)
		signal(SIGABRT, clean_exit);
	if (sig == SIGKILL)
		signal(SIGKILL, clean_exit);

	printf("Caught exit signal\n");
	close(get_config_info()->server_infos[PROCESS_ID].self_sid);
	close(get_config_info()->server_infos[PROCESS_ID].successor_sid);
	free(data);
	exit(0);
}
void enable_ctrl_c_handler() {
	signal(SIGINT, clean_exit);
	signal(SIGABRT, clean_exit);
	signal(SIGKILL, clean_exit);
}
